//---------------------------------------------------------------------
// This file is part of the Microsoft BizTalk Server 2006 SDK
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This source code is intended only as a supplement to Microsoft BizTalk
// Server 2006 release and/or on-line documentation. See these other
// materials for detailed information regarding Microsoft code samples.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Diagnostics;
using System.Collections.Specialized;
using System.Resources;
using System.Reflection;
using System.Text;
using System.Xml;
using EnvDTE;
using VSLangProj;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;

namespace MsPartners.Unisys.BizTalkAdapters.AdapterWizard
{
	/// <summary>
	/// Summary description for RenderHelper.
	/// </summary>
	internal class ResourceTemplate
	{
		private List<AdapterProperty> transmitHandlerProperties = null;
		private List<AdapterProperty> transmitEndpointProperties = null;
		private List<AdapterProperty> receiveHandlerProperties = null;
		private List<AdapterProperty> receiveEndpointProperties = null;

		protected string transportType = null;
		protected string newProjectNamespace = null;
		protected string thisProjectNamespace = null;
		protected string propertyNamespace = null;
		protected string outboundCLSID = null;
		protected string inboundCLSID = null;
		protected string transmitAdapterDescription = null;
		protected string receiveAdapterDescription = null;
		protected bool receiveAdapterIsInProcess = false;
		protected bool receiveAdapterSupportsReqRsp = false;
		private bool transmitAdapterhasExtendedBatchSupport = false;


		private string xsdPropertyString =
			"<xs:element name=\"{0}\" type=\"{1}\">" + Environment.NewLine +
				"<xs:annotation>" + Environment.NewLine +
					"<xs:appinfo>" + Environment.NewLine +
						"<baf:designer>" + Environment.NewLine +
							"<baf:displayname _locID=\"{2}\">Edit this field in the resource file</baf:displayname>" + Environment.NewLine +
							"<baf:description _locID=\"{3}\">Edit this field in the resource file</baf:description>" + Environment.NewLine +
						"</baf:designer>" + Environment.NewLine +
					"</xs:appinfo>" + Environment.NewLine +
				"</xs:annotation>" + Environment.NewLine +
			"</xs:element>" + Environment.NewLine;

		public ResourceTemplate()
		{
			thisProjectNamespace = "MsPartners.Unisys.BizTalkAdapters.AdapterWizard";
		}

		protected virtual string GetCodeTransmitHandlerMemberString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeTransmitHandlerExtractString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeTransmitHandlerPropertiesString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeTransmitEndPointMemberString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeTransmitEndPointPropertiesString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeTransmitEndpointExtractString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeTransmitLocationURIString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeReceiveHandlerMemberString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeReceiveHandlerExtractString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeReceiveHandlerPropertiesString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeReceiveEndPointMemberString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeReceiveEndPointPropertiesString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeReceiveEndpointExtractString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeReceiveLocationURIString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeDesignTimeValidationString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeDesignTimeURIString(List<AdapterProperty> properties) { return null; }
		protected virtual string GetCodeHandlerMemberString(List<AdapterProperty> properties) { return null; }


		public void RenderAndAddToProject(string resourceName,
			string relativePath,
			string designTimeDir,
			VSProject AdapterVSProject,
			bool Embedded,
			bool AddTransportToName)
		{
			string NewFileName = Render(resourceName, relativePath, designTimeDir,
				AddTransportToName);
			EnvDTE.ProjectItem AdapterProjectItem =
				AdapterVSProject.Project.ProjectItems.AddFromFile(NewFileName);
			if (Embedded)
			{
				AdapterProjectItem.Properties.Item("BuildAction").Value =
					prjBuildAction.prjBuildActionEmbeddedResource;
			}
		}

		/// <summary>
		/// Renders the template files into a compilable sourcefile.
		/// Replaces code-markers with values taken from the wizard pages. 
		/// </summary>
		/// <param ap.Name="resourceName">Name of the code-template</param>
		/// <param ap.Name="relativePath">Relative path of the resource inside this project</param>
		/// <param ap.Name="destDir">The dir where the file will be placed</param>
		/// <param ap.Name="AddTransportToName">If the TransportType should be added to the filename</param>
		/// <returns></returns>
		public string Render(string resourceName, string relativePath, string destDir, bool AddTransportToName)
		{
			string newFileName = null;
			string oldFileName = resourceName.Replace("._", ".");

			if (AddTransportToName)
				newFileName = destDir + @"\" + transportType + oldFileName;
			else
				newFileName = destDir + @"\" + oldFileName;

			//Remove prefixes for different receive adapter implementations
			newFileName = newFileName.Replace("IH__", "");
			newFileName = newFileName.Replace("IP__", "");

			string fileContent = GetResource(thisProjectNamespace + "." + relativePath + "." + resourceName);
			//  Replace the template fields in the file with the appropriate values 

			fileContent = fileContent.Replace("[!output PROPERTYNAMESPACE]", propertyNamespace);
			fileContent = fileContent.Replace("[!output OUTBOUNDGUID]", outboundCLSID);
			fileContent = fileContent.Replace("[!output INBOUNDGUID]", inboundCLSID);
			fileContent = fileContent.Replace("[!output NAMESPACE]", newProjectNamespace);
			fileContent = fileContent.Replace("[!output TRANSPORTNAME]", transportType);
			fileContent = fileContent.Replace("[!output PROJECTGUID]", Guid.NewGuid().ToString());
			fileContent = fileContent.Replace("[!output TRANSMIT_DESCRIPTION]", transmitAdapterDescription);
			fileContent = fileContent.Replace("[!output RECEIVE_DESCRIPTION]", receiveAdapterDescription);
			fileContent = fileContent.Replace("[!output RESPONSEHANDLER_CODE]", GetCodeReceiveResponseHandler());

			fileContent = fileContent.Replace("[!output TYPEOF_TRANSMITTER_ENDPOINT]", GetCodeTransmitEndpointType());
			fileContent = fileContent.Replace("[!output TRANSMITTER_RETURNBATCH]", GetCodeTransmitTransmitBatchType());

			//Transmit handler xsd
			if (fileContent.IndexOf("[!output TRANSMIT_HANDLERPROPERTY_XSD]") > 0)
			{
				fileContent = fileContent.Replace("[!output TRANSMIT_HANDLERPROPERTY_XSD]",
					GetXsdPropertiesString(transmitHandlerProperties));
			}

			//Transmit handler property class
			if (fileContent.IndexOf("[!output TRANSMIT_HANDLERMEMBERS_CODE]") > 0)
			{
				fileContent = fileContent.Replace("[!output TRANSMIT_HANDLERMEMBERS_CODE]",
					GetCodeHandlerMemberString(transmitHandlerProperties));

				fileContent = fileContent.Replace("[!output TRANSMIT_HANDLERPROPERTIES_CODE]",
					GetCodeTransmitHandlerPropertiesString(transmitHandlerProperties));

				fileContent = fileContent.Replace("[!output TRANSMIT_HANDLEREXTRACT_CODE]",
					GetCodeTransmitHandlerExtractString(transmitHandlerProperties));
			}

			//Transmit endpoint xsd
			if (fileContent.IndexOf("[!output TRANSMIT_ENDPOINTPROPERTY_XSD]") > 0)
			{
				fileContent = fileContent.Replace("[!output TRANSMIT_ENDPOINTPROPERTY_XSD]",
					GetXsdPropertiesString(transmitEndpointProperties));

			}

			//Transmit endpoint property class
			if (fileContent.IndexOf("[!output TRANSMIT_ENDPOINTMEMBERS_CODE]") > 0)
			{
				fileContent = fileContent.Replace("[!output TRANSMIT_ENDPOINTMEMBERS_CODE]",
					GetCodeTransmitEndPointMemberString(transmitEndpointProperties));

				fileContent = fileContent.Replace("[!output TRANSMIT_ENDPOINTPROPERTIES_CODE]",
					GetCodeTransmitEndPointPropertiesString(transmitEndpointProperties));

				fileContent = fileContent.Replace("[!output TRANSMIT_ENDPOINTEXTRACT_CODE]",
					GetCodeTransmitEndpointExtractString(transmitEndpointProperties));
			}

			//Transmit endpoint validation code in the adapter management code
			if (fileContent.IndexOf("[!output TRANSMIT_VALIDATIONS_CODE]") > 0)
			{
				fileContent = fileContent.Replace("[!output TRANSMIT_VALIDATIONS_CODE]",
					GetCodeDesignTimeValidationString(transmitEndpointProperties));

				fileContent = fileContent.Replace("[!output CREATE_TRANSMIT_URI_CODE]",
					GetCodeDesignTimeURIString(transmitEndpointProperties));
			}


			//Receive handler xsd
			if (fileContent.IndexOf("[!output RECEIVE_HANDLERPROPERTY_XSD]") > 0)
			{
				fileContent = fileContent.Replace("[!output RECEIVE_HANDLERPROPERTY_XSD]",
					GetXsdPropertiesString(receiveHandlerProperties));
			}

			//Receive handler property class
			if (fileContent.IndexOf("[!output RECEIVE_HANDLERMEMBERS_CODE]") > 0)
			{
				fileContent = fileContent.Replace("[!output RECEIVE_HANDLERMEMBERS_CODE]",
					GetCodeHandlerMemberString(receiveHandlerProperties));

				fileContent = fileContent.Replace("[!output RECEIVE_HANDLERPROPERTIES_CODE]",
					GetCodeReceiveHandlerPropertiesString(receiveHandlerProperties));

				fileContent = fileContent.Replace("[!output RECEIVE_HANDLEREXTRACT_CODE]",
					GetCodeReceiveHandlerExtractString(receiveHandlerProperties));
			}

			//Receive endpoint xsd
			if (fileContent.IndexOf("[!output RECEIVE_ENDPOINTPROPERTY_XSD]") > 0)
			{
				fileContent = fileContent.Replace("[!output RECEIVE_ENDPOINTPROPERTY_XSD]",
					GetXsdPropertiesString(receiveEndpointProperties));

			}

			//Receive endpoint property class
			if (fileContent.IndexOf("[!output RECEIVE_ENDPOINTMEMBERS_CODE]") > 0)
			{
				fileContent = fileContent.Replace("[!output RECEIVE_ENDPOINTMEMBERS_CODE]",
					GetCodeReceiveEndPointMemberString(receiveEndpointProperties));

				fileContent = fileContent.Replace("[!output RECEIVE_ENDPOINTPROPERTIES_CODE]",
					GetCodeReceiveEndPointPropertiesString(receiveEndpointProperties));

				fileContent = fileContent.Replace("[!output RECEIVE_ENDPOINTEXTRACT_CODE]",
					GetCodeReceiveEndpointExtractString(receiveEndpointProperties));
			}

			//Receive endpoint validation code in the adapter management code
			if (fileContent.IndexOf("[!output RECEIVE_VALIDATIONS_CODE]") > 0)
			{
				fileContent = fileContent.Replace("[!output RECEIVE_VALIDATIONS_CODE]",
					GetCodeDesignTimeValidationString(receiveEndpointProperties));

				fileContent = fileContent.Replace("[!output CREATE_RECEIVE_URI_CODE]",
					GetCodeDesignTimeURIString(receiveEndpointProperties));
			}

			//Resource.resx file
			if (fileContent.IndexOf("[!output RESOURCE_META_DATA]") > 0)
			{
				fileContent = fileContent.Replace("[!output RESOURCE_META_DATA]",
					GetXmlResourceMetaData());
			}

			//  Write the string back out to the new file in our solution 
			using (TextWriter tw = new StreamWriter(newFileName, false, Encoding.UTF8))
			{
				tw.Write(fileContent);
				tw.Close();
			}
			return newFileName;

		}

		/// <summary>
		/// Creates a piece of XSD code for the designTime xsd
		/// </summary>
		/// <returns></returns>
		protected string GetXsdPropertiesString(List<AdapterProperty> properties)
		{
			if (properties == null || properties.Count == 0)
				return "";
			string result = "";
			foreach (AdapterProperty ap in properties)
			{
				result += string.Format(xsdPropertyString,
					ap.Name,
					ap.DataType,
					ap.Name + "Name",
					ap.Name + "Desc");
			}
			return result;
		}

		/// <summary>
		/// Creates a piece of XSD code for the designTime endpoint xsd
		/// </summary>
		/// <returns></returns>
		protected string GetXsdReceiveEndPointPropertiesString()
		{
			string result = "";
			foreach (AdapterProperty ap in receiveEndpointProperties)
			{
				result += string.Format(xsdPropertyString, ap.Name, ap.DataType, ap.Name + "Name", ap.Name + "Desc");
			}
			if (receiveAdapterSupportsReqRsp)
			{
				result += string.Format(xsdPropertyString, "timeout", "xs:int");

			}
			return result;
		}

		/// <summary>
		/// Helper to get resource from manifest.
		/// </summary>
		/// <param ap.Name="resource">Full resource ap.Name</param>
		/// <returns>Resource propValue</returns>
		protected string GetResource(string resource)
		{
			string value = null;
			if (null != resource)
			{
				Assembly assem = this.GetType().Assembly;
				Stream stream = assem.GetManifestResourceStream(resource);
				Trace.WriteLine(resource);
				StreamReader reader = null;

				using (reader = new StreamReader(stream))
				{
					value = reader.ReadToEnd();
				}
			}
			return value;
		}

		/// <summary>
		/// Creates an extractfunction based on dataType
		/// </summary>
		/// <param ap.Name="dataType"></param>
		/// <returns></returns>
		protected string GetAppropriateExtractFunction(string dataType, string valueName)
		{
			switch (dataType)
			{
				case "xs:string":
					return "Extract(configDOM, \"/Config/" + valueName + "\", String.Empty)";

				case "xs:boolean":
					return "ExtractBool(configDOM, \"/Config/" + valueName + "\")";

				case "xs:int":
					return "ExtractInt(configDOM, \"/Config/" + valueName + "\")";

				case "xs:long":
					return "ExtractLong(configDOM, \"/Config/" + valueName + "\")";
				default:
					return "Extract(configDOM, \"/Config/" + valueName + "\")";
			}
		}

		/// <summary>
		/// When the in-process adapter supports request response this code is inserted.
		/// </summary>
		/// <returns></returns>
		protected string GetCodeReceiveResponseHandler()
		{
			const string tabs = "\t\t\t\t\t\t\t";
			if (ReceiveAdapterSupportsReqRsp)
			{
				return "DateTime expDateTime = DateTime.Now.AddSeconds(properties.Timeout);" + Environment.NewLine +
					tabs + transportType + "ResponseHandler responseHandler = new " + transportType + "ResponseHandler(this.transportProxy);" + Environment.NewLine +
					tabs + "batch.SubmitRequestMessage(btsMsg, Guid.NewGuid().ToString(\"D\"), true, expDateTime, responseHandler);" + Environment.NewLine +
					tabs + "" + Environment.NewLine +
					tabs + "// Done will call \"Enter\" on the terminator to ensure proper ref-counting" + Environment.NewLine +
					tabs + "batch.Done();" + Environment.NewLine +
					tabs + "" + Environment.NewLine +
					tabs + "// Wait for the callback (this should always complete quickly)" + Environment.NewLine +
					tabs + "if (!batch.Wait())" + Environment.NewLine +
					tabs + "{" + Environment.NewLine +
					tabs + "\tthrow new Exception(\"Failed to submit the " + transportType + " request to BizTalk Server\");" + Environment.NewLine +
					tabs + "}" + Environment.NewLine +
					tabs + "" + Environment.NewLine +
					tabs + "// It's a good idea to have a timeout on the response message" + Environment.NewLine +
					tabs + "IBaseMessage responseMsg = responseHandler.WaitForResponse(properties.Timeout);" + Environment.NewLine +
					tabs + "if (responseMsg == null)" + Environment.NewLine +
					tabs + "{" + Environment.NewLine +
					tabs + "\t// Response did not arrive in time!" + Environment.NewLine +
					tabs + "\tthrow new Exception(\"Failed to retrieve the " + transportType + " response message from BizTalk Server\");" + Environment.NewLine +
					tabs + "}" + Environment.NewLine;
			}
			else
			{
				return "// Put your reponse handler code here, if needed." + Environment.NewLine;
			}
		}

		protected string GetCodeTransmitEndpointType()
		{
			if (transmitAdapterhasExtendedBatchSupport)
				return "null";
			else
				return "typeof(" + transportType + "TransmitterEndpoint)";
		}

		protected string GetCodeTransmitTransmitBatchType()
		{
			if (transmitAdapterhasExtendedBatchSupport)
				return "return new " + transportType + "TransmitAsyncBatch(this.TransportProxy, this.ControlledTermination, this.PropertyNamespace, this.MaxBatchSize);";
			else
				return "return new " + transportType + "TransmitAdapterBatch(this.MaxBatchSize, " + transportType + "Namespace, TransportProxy, this);";
		}

		protected string GetXmlResourceMetaData()
		{
			string result = "";
			string metadata =
				"<data name=\"{0}Name\" xml:space=\"preserve\">" + Environment.NewLine +
				"<value>{1}</value>" + Environment.NewLine +
				"</data>" + Environment.NewLine +
			"<data name=\"{0}Desc\" xml:space=\"preserve\">" + Environment.NewLine +
				"<value>{2}</value>" + Environment.NewLine +
				"</data>" + Environment.NewLine;

			List<AdapterProperty> adapterPropertyList = new List<AdapterProperty>();
			if (transmitEndpointProperties != null)
				adapterPropertyList.AddRange(transmitEndpointProperties);
			if (transmitHandlerProperties != null)
				adapterPropertyList.AddRange(transmitHandlerProperties);
			if (receiveEndpointProperties != null)
				adapterPropertyList.AddRange(receiveEndpointProperties);
			if (receiveHandlerProperties != null)
				adapterPropertyList.AddRange(receiveHandlerProperties);

			string friendlyName = null;

			foreach (AdapterProperty ap in adapterPropertyList)
			{
				if (ap.FriendlyName.Length == 0)
					friendlyName = ap.Name;
				else
					friendlyName = ap.FriendlyName;

				result += string.Format(metadata, ap.Name, friendlyName, ap.Description);
			}
			return result;
		}

		protected string UncapitalizeFirstLetter(string memberName)
		{
			return memberName.Substring(0, 1).ToLower() + memberName.Substring(1);
		}

		protected string CapitalizeFirstLetter(string memberName)
		{
			return memberName.Substring(0, 1).ToUpper() + memberName.Substring(1);
		}

		public List<AdapterProperty> ReceiveHandlerProperties
		{
			get { return receiveHandlerProperties; }
			set { receiveHandlerProperties = value; }
		}

		public List<AdapterProperty> TransmitEndpointProperties
		{
			get { return transmitEndpointProperties; }
			set { transmitEndpointProperties = value; }
		}

		public List<AdapterProperty> ReceiveEndpointProperties
		{
			get { return receiveEndpointProperties; }
			set { receiveEndpointProperties = value; }
		}

		public List<AdapterProperty> TransmitHandlerProperties
		{
			get { return transmitHandlerProperties; }
			set { transmitHandlerProperties = value; }
		}

		public string NewProjectNamespace
		{
			get { return newProjectNamespace; }
			set { newProjectNamespace = value; }
		}

		public string PropertyNamespace
		{
			get { return propertyNamespace; }
			set { propertyNamespace = value; }
		}

		public string TransportType
		{
			get { return transportType; }
			set { transportType = value; }
		}

		public string OutboundCLSID
		{
			get { return outboundCLSID; }
			set { outboundCLSID = value; }
		}

		public string ThisProjectNamespace
		{
			get { return thisProjectNamespace; }
			set { thisProjectNamespace = value; }
		}

		public string InboundCLSID
		{
			get { return inboundCLSID; }
			set { inboundCLSID = value; }
		}

		public string ReceiveAdapterDescription
		{
			get { return receiveAdapterDescription; }
			set { receiveAdapterDescription = value; }
		}

		public string TransmitAdapterDescription
		{
			get { return transmitAdapterDescription; }
			set { transmitAdapterDescription = value; }
		}

		public bool ReceiveAdapterIsInProcess
		{
			get { return this.receiveAdapterIsInProcess; }
			set { this.receiveAdapterIsInProcess = value; }
		}

		public bool ReceiveAdapterSupportsReqRsp
		{
			get { return this.receiveAdapterSupportsReqRsp; }
			set { this.receiveAdapterSupportsReqRsp = value; }
		}

		public bool TransmitAdapterhasExtendedBatchSupport
		{
			get { return transmitAdapterhasExtendedBatchSupport; }
			set { transmitAdapterhasExtendedBatchSupport = value; }
		}
	}
}
